"require-dev": {
"jakub-onderka/php-parallel-lint": "~0.8",
"justinrainbow/json-schema": "~1.3",
- "phpunit/phpunit": "~4.5",
+ "phpunit/phpunit": "3.7.37",
"mediawiki/mediawiki-codesniffer": "0.1.0"
},
"suggest": {
"description": "Extension's authors.",
"items": {
"type": "string"
- },
- "additionalItems": false
+ }
},
"version": {
"type": "string",
"apihelp-main-param-action": "Which action to perform.",
"apihelp-main-param-format": "The format of the output.",
"apihelp-main-param-maxlag": "Maximum lag can be used when MediaWiki is installed on a database replicated cluster. To save actions causing any more site replication lag, this parameter can make the client wait until the replication lag is less than the specified value. In case of excessive lag, error code <samp>maxlag</samp> is returned with a message like <samp>Waiting for $host: $lag seconds lagged</samp>.<br />See [[mw:Manual:Maxlag_parameter|Manual: Maxlag parameter]] for more information.",
- "apihelp-main-param-smaxage": "Set the <code>s-maxage</code> header to this many seconds. Errors are never cached.",
- "apihelp-main-param-maxage": "Set the <code>max-age</code> header to this many seconds. Errors are never cached.",
+ "apihelp-main-param-smaxage": "Set the <code>s-maxage</code> HTTP cache control header to this many seconds. Errors are never cached.",
+ "apihelp-main-param-maxage": "Set the <code>max-age</code> HTTP cache control header to this many seconds. Errors are never cached.",
"apihelp-main-param-assert": "Verify the user is logged in if set to <kbd>user</kbd>, or has the bot userright if <kbd>bot</kbd>.",
"apihelp-main-param-requestid": "Any value given here will be included in the response. May be used to distinguish requests.",
"apihelp-main-param-servedby": "Include the hostname that served the request in the results.",
return false;
}
- wfDebugLog( 'connect', __METHOD__ .
- ": Using reader #$i: {$this->mServers[$i]['host']}..." );
+ $serverName = $this->getServerName( $i );
+ wfDebugLog( 'connect', __METHOD__ . ": Using reader #$i: $serverName..." );
$conn = $this->openConnection( $i, $wiki );
if ( !$conn ) {
if ( $result == -1 || is_null( $result ) ) {
# Timed out waiting for slave, use master instead
- $server = $this->mServers[$index]['host'];
+ $server = $server = $this->getServerName( $index );
$msg = __METHOD__ . ": Timed out waiting on $server pos {$this->mWaitForPos}";
wfDebug( "$msg\n" );
wfDebugLog( 'DBPerformance', "$msg:\n" . wfBacktrace( true ) );
$server = $this->mServers[$i];
$server['serverIndex'] = $i;
$conn = $this->reallyOpenConnection( $server, false );
+ $serverName = $this->getServerName( $i );
if ( $conn->isOpen() ) {
- wfDebug( "Connected to database $i at {$this->mServers[$i]['host']}\n" );
+ wfDebug( "Connected to database $i at $serverName\n" );
$this->mConns['local'][$i][0] = $conn;
} else {
- wfDebug( "Failed to connect to database $i at {$this->mServers[$i]['host']}\n" );
+ wfDebug( "Failed to connect to database $i at $serverName\n" );
$this->mErrorConnection = $conn;
$conn = false;
}
*/
public function getServerName( $i ) {
if ( isset( $this->mServers[$i]['hostName'] ) ) {
- return $this->mServers[$i]['hostName'];
+ $name = $this->mServers[$i]['hostName'];
} elseif ( isset( $this->mServers[$i]['host'] ) ) {
- return $this->mServers[$i]['host'];
+ $name = $this->mServers[$i]['host'];
} else {
- return '';
+ $name = '';
}
+
+ return ( $name != '' ) ? $name : 'localhost';
}
/**
$headers['Date'] = MWTimestamp::getLocalInstance()->format( 'r' );
$headers['Message-ID'] = self::makeMsgId();
$headers['X-Mailer'] = 'MediaWiki mailer';
+ $headers['List-Unsubscribe'] = '<' . SpecialPage::getTitleFor( 'Preferences' )
+ ->getFullURL( '', false, PROTO_CANONICAL ) . '>';
# Line endings need to be different on Unix and Windows due to
# the bug described at http://trac.wordpress.org/ticket/2603
$dbw->begin( __METHOD__ );
if ( $id == 0 ) {
- $this->loadPageData( 'forupdate' );
+ // T98706: lock the page from various other updates but avoid using
+ // WikiPage::READ_LOCKING as that will carry over the FOR UPDATE to
+ // the revisions queries (which also JOIN on user). Only lock the page
+ // row and CAS check on page_latest to see if the trx snapshot matches.
+ $latest = $this->lock();
+
+ $this->loadPageData( WikiPage::READ_LATEST );
$id = $this->getID();
- if ( $id == 0 ) {
+ if ( $id == 0 || $this->getLatest() != $latest ) {
+ // Page not there or trx snapshot is stale
$dbw->rollback( __METHOD__ );
$status->error( 'cannotdelete', wfEscapeWikiText( $this->getTitle()->getPrefixedText() ) );
return $status;
return $status;
}
+ /**
+ * Lock the page row for this title and return page_latest (or 0)
+ *
+ * @return integer
+ */
+ protected function lock() {
+ return (int)wfGetDB( DB_MASTER )->selectField(
+ 'page',
+ 'page_latest',
+ array(
+ 'page_namespace' => $this->getTitle()->getNamespace(),
+ 'page_title' => $this->getTitle()->getDBkey()
+ ),
+ __METHOD__,
+ array( 'FOR UPDATE' )
+ );
+ }
+
/**
* Do some database updates after deletion
*
static $table = array(
array( '', 'א', 'ב', 'ג', 'ד', 'ה', 'ו', 'ז', 'ח', 'ט', 'י' ),
array( '', 'י', 'כ', 'ל', 'מ', 'נ', 'ס', 'ע', 'פ', 'צ', 'ק' ),
- array( '', 'ק', 'ר', 'ש', 'ת', 'תק', 'תר', 'תש', 'תת', 'תתק', 'תתר' ),
+ array( '',
+ array( 'ק' ),
+ array( 'ר' ),
+ array( 'ש' ),
+ array( 'ת' ),
+ array( 'ת', 'ק' ),
+ array( 'ת', 'ר' ),
+ array( 'ת', 'ש' ),
+ array( 'ת', 'ת' ),
+ array( 'ת', 'ת', 'ק' ),
+ array( 'ת', 'ת', 'ר' ),
+ ),
array( '', 'א', 'ב', 'ג', 'ד', 'ה', 'ו', 'ז', 'ח', 'ט', 'י' )
);
return $num;
}
- $s = '';
+ // Round thousands have special notations
+ if ( $num === 1000 ) {
+ return "א' אלף";
+ } elseif ( $num % 1000 === 0 ) {
+ return $table[0][$num / 1000] . "' אלפים";
+ }
+
+ $letters = array();
+
for ( $pow10 = 1000, $i = 3; $i >= 0; $pow10 /= 10, $i-- ) {
if ( $num >= $pow10 ) {
- if ( $num == 15 || $num == 16 ) {
- $s .= $table[0][9] . $table[0][$num - 9];
+ if ( $num === 15 || $num === 16 ) {
+ $letters[] = $table[0][9];
+ $letters[] = $table[0][$num - 9];
$num = 0;
} else {
- $s .= $table[$i][intval( ( $num / $pow10 ) )];
- if ( $pow10 == 1000 ) {
- $s .= "'";
+ $letters = array_merge(
+ $letters,
+ (array)$table[$i][intval( $num / $pow10 )]
+ );
+
+ if ( $pow10 === 1000 ) {
+ $letters[] = "'";
}
}
}
+
$num = $num % $pow10;
}
- if ( strlen( $s ) == 2 ) {
- $str = $s . "'";
+
+ $preTransformLength = count( $letters );
+ if ( $preTransformLength === 1 ) {
+ // Add geresh (single quote) to one-letter numbers
+ $letters[] = "'";
} else {
- $str = substr( $s, 0, strlen( $s ) - 2 ) . '"';
- $str .= substr( $s, strlen( $s ) - 2, 2 );
- }
- $start = substr( $str, 0, strlen( $str ) - 2 );
- $end = substr( $str, strlen( $str ) - 2 );
- switch ( $end ) {
- case 'כ':
- $str = $start . 'ך';
- break;
- case 'מ':
- $str = $start . 'ם';
- break;
- case 'נ':
- $str = $start . 'ן';
- break;
- case 'פ':
- $str = $start . 'ף';
- break;
- case 'צ':
- $str = $start . 'ץ';
- break;
+ $lastIndex = $preTransformLength - 1;
+ $letters[$lastIndex] = str_replace(
+ array( 'כ', 'מ', 'נ', 'פ', 'צ' ),
+ array( 'ך', 'ם', 'ן', 'ף', 'ץ' ),
+ $letters[$lastIndex]
+ );
+
+ // Add gershayim (double quote) to multiple-letter numbers,
+ // but exclude numbers with only one letter after the thousands
+ // (1001-1009, 1020, 1030, 2001-2009, etc.)
+ if ( $letters[1] === "'" && $preTransformLength === 3 ) {
+ $letters[] = "'";
+ } else {
+ array_splice( $letters, -1, 0, '"' );
+ }
}
- return $str;
+
+ return implode( $letters );
}
/**
// we hide the input element as instead we will style the label that follows
// we use opacity so that VoiceOver software can still identify it
opacity: 0;
+ // Render "on top of" the label, so that it's still clickable (T98905)
+ z-index: 1;
+ position: relative;
// ensure the invisible checkbox takes up the required width
width: @checkboxSize;
height: @checkboxSize;
);
}
+ /**
+ * @dataProvider provideHebrewNumeralsData
+ * @covers Language::hebrewNumeral
+ */
+ public function testHebrewNumeral( $num, $numerals ) {
+ $this->assertEquals(
+ $numerals,
+ Language::hebrewNumeral( $num ),
+ "hebrewNumeral('$num')"
+ );
+ }
+
+ public static function provideHebrewNumeralsData() {
+ return array(
+ array( -1, -1 ),
+ array( 0, 0 ),
+ array( 1, "א'" ),
+ array( 2, "ב'" ),
+ array( 3, "ג'" ),
+ array( 4, "ד'" ),
+ array( 5, "ה'" ),
+ array( 6, "ו'" ),
+ array( 7, "ז'" ),
+ array( 8, "ח'" ),
+ array( 9, "ט'" ),
+ array( 10, "י'" ),
+ array( 11, 'י"א' ),
+ array( 14, 'י"ד' ),
+ array( 15, 'ט"ו' ),
+ array( 16, 'ט"ז' ),
+ array( 17, 'י"ז' ),
+ array( 20, "כ'" ),
+ array( 21, 'כ"א' ),
+ array( 30, "ל'" ),
+ array( 40, "מ'" ),
+ array( 50, "נ'" ),
+ array( 60, "ס'" ),
+ array( 70, "ע'" ),
+ array( 80, "פ'" ),
+ array( 90, "צ'" ),
+ array( 99, 'צ"ט' ),
+ array( 100, "ק'" ),
+ array( 101, 'ק"א' ),
+ array( 110, 'ק"י' ),
+ array( 200, "ר'" ),
+ array( 300, "ש'" ),
+ array( 400, "ת'" ),
+ array( 500, 'ת"ק' ),
+ array( 800, 'ת"ת' ),
+ array( 1000, "א' אלף" ),
+ array( 1001, "א'א'" ),
+ array( 1012, "א'י\"ב" ),
+ array( 1020, "א'ך'" ),
+ array( 1030, "א'ל'" ),
+ array( 1081, "א'פ\"א" ),
+ array( 2000, "ב' אלפים" ),
+ array( 2016, "ב'ט\"ז" ),
+ array( 3000, "ג' אלפים" ),
+ array( 4000, "ד' אלפים" ),
+ array( 4904, "ד'תתק\"ד" ),
+ array( 5000, "ה' אלפים" ),
+ array( 5680, "ה'תר\"ף" ),
+ array( 5690, "ה'תר\"ץ" ),
+ array( 5708, "ה'תש\"ח" ),
+ array( 5720, "ה'תש\"ך" ),
+ array( 5740, "ה'תש\"ם" ),
+ array( 5750, "ה'תש\"ן" ),
+ array( 5775, "ה'תשע\"ה" ),
+ );
+ }
+
/**
* @dataProvider providePluralData
* @covers Language::convertPlural